#include <stdint.h>
#include "stm32f4xx_hal.h"
#include "Drv_I2C.h"
#include "Drv_OLED.h"


static uint8_t g_ScreenBuffer[SCREEN_PAGE_NUM][SCREEN_COLUMN];

/**
***********************************************************
* @brief OLED写入命令
* @param cmd:命令字
* @return 
***********************************************************
*/
static void OLED_WriteCommand(uint8_t cmd)
{
	I2C_WriteByte(CMD_REG_ADDRESS, cmd);
}

/**
***********************************************************
* @brief OLED写入数据
* @param data:数据
* @return 
***********************************************************
*/
static void OLED_WriteData(uint8_t data)
{
	I2C_WriteByte(DATA_REG_ADDRESS, data);
}


/**
***********************************************************
* @brief I2C硬件初始化
* @param
* @return 
***********************************************************
*/
void OLED_DrvInit(void)
{
	I2C_DrvInit(OLED_Device_Address);
	
	/* 参数初始化 */
    OLED_WriteCommand(0xAE);//--turn off oled panel
	OLED_WriteCommand(0x00);//---set low column address
	OLED_WriteCommand(0x10);//---set high column address
	OLED_WriteCommand(0x40);//--set start line address  Set Mapping RAM Display Start Line 
	OLED_WriteCommand(0x81);//--set contrast control register
	OLED_WriteCommand(0xCF);// Set SEG Output Current Brightness
	OLED_WriteCommand(0xA1);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WriteCommand(0xC8);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WriteCommand(0xA6);//--set normal display
	OLED_WriteCommand(0xA8);//--set multiplex ratio(1 to 64)
	OLED_WriteCommand(0x3f);//--1/64 duty
	OLED_WriteCommand(0xD3);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	OLED_WriteCommand(0x00);//-not offset
	OLED_WriteCommand(0xd5);//--set display clock divide ratio/oscillator frequency
	OLED_WriteCommand(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WriteCommand(0xD9);//--set pre-charge period
	OLED_WriteCommand(0xF1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WriteCommand(0xDA);//--set com pins hardware configuration
	OLED_WriteCommand(0x12);
	OLED_WriteCommand(0xDB);//--set vcomh
	OLED_WriteCommand(0x40);//Set VCOM Deselect Level
	OLED_WriteCommand(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)
	OLED_WriteCommand(0x02);//
	OLED_WriteCommand(0x8D);//--set Charge Pump enable/disable
	OLED_WriteCommand(0x14);//--set(0x10) disable
	OLED_WriteCommand(0xA4);// Disable Entire Display On (0xa4/0xa5)
	OLED_WriteCommand(0xA6);// Disable Inverse Display On (0xa6/a7) 
	
	OLED_Clear();
	OLED_WriteCommand(0xAF);
}

/**
***********************************************************
* @brief 开启OLED显示
* @param
* @return 
***********************************************************
*/    
void OLED_DisplayOn(void)
{
	OLED_WriteCommand(0X8D);  //SET DCDC命令
	OLED_WriteCommand(0X14);  //DCDC ON
	OLED_WriteCommand(0XAF);  //DISPLAY ON
}
 
/**
***********************************************************
* @brief 关闭OLED显示
* @param
* @return 
***********************************************************
*/    
void OLED_DisplayOff(void)
{
	OLED_WriteCommand(0X8D);  //SET DCDC命令
	OLED_WriteCommand(0X10);  //DCDC OFF
	OLED_WriteCommand(0XAE);  //DISPLAY OFF
}

/**
***********************************************************
* @brief 清屏显示
* @param
* @return 
***********************************************************
*/ 
void OLED_Clear(void)
{
	uint8_t i, n;		    
	for(i = 0; i < SCREEN_PAGE_NUM; i++)  
	{  
		OLED_WriteCommand(0xb0 + i);
		OLED_WriteCommand(0x00); 
		OLED_WriteCommand(0x10); 
		for(n = 0; n < SCREEN_PAGEDATA_NUM; n++)
		{
			OLED_WriteData(0x00);
		}
	} 
}

/**
 * ************************************************************************
 * @brief 更新数据缓冲区
 * ************************************************************************
 */
void OLED_RefreshRAM(void)
{
	// 页寻址模式填充
	for(uint16_t m = 0; m < SCREEN_ROW / 8; m++)
	{
		OLED_WriteCommand(0xb0 + m);		//设置页地址b0~b7
		OLED_WriteCommand(0x00);			//设置显示位置—列低地址00-0f
		OLED_WriteCommand(0x10);			//设置显示位置—列高地址10-1f
		for(uint16_t n = 0; n < SCREEN_COLUMN; n++)
		{
			OLED_WriteData(g_ScreenBuffer[m][n]);
		}
	} 
}

/**
 * ************************************************************************
 * @brief 清除数据缓冲区
 * ************************************************************************
 */
void OLED_ClearRAM(void)
{
	for(uint16_t m = 0; m < SCREEN_ROW / 8; m++)
	{
		for(uint16_t n = 0; n < SCREEN_COLUMN; n++)
		{
			g_ScreenBuffer[m][n] = 0x00;
		}
	}
}

/**
 * ************************************************************************
 * @brief 设置坐标像素点数据
 * 
 * @param[in] x  			起始点横坐标(x:0~127）
 * @param[in] y  			起始点纵坐标(y:0~63）
 * @param[in] set_pixel  	该点的数据  SET_PIXEL = 1, RESET_PIXEL = 0
 * 
 * ************************************************************************
 */
void OLED_SetPixel(int16_t x, int16_t y, uint8_t set_pixel)
{ 
	if (x >= 0 && x < SCREEN_COLUMN && y >= 0 && y < SCREEN_ROW)
	{
		if(set_pixel)
		{
			g_ScreenBuffer[y/8][x] |= (0x01 << (y%8));
		}  
		else
		{
			g_ScreenBuffer[y/8][x] &= ~(0x01 << (y%8));
		}
	}
}

/**
 * ************************************************************************
 * @brief 屏幕内容取反显示
 * 
 * @param[in] mode  开关
 * 					开	ON	0xA7	点亮全屏
 *  				关	OFF	0xA6	默认此模式，设置像素点亮
 * 
 * ************************************************************************
 */
void OLED_DisplayMode(uint8_t mode)
{
	OLED_WriteCommand(mode);
}

/**
 * ************************************************************************
 * @brief 屏幕亮度调节
 * 
 * @param[in] intensity  亮度大小（0~255），默认为0x7f
 * 
 * ************************************************************************
 */
void OLED_IntensityControl(uint8_t intensity)
{
	OLED_WriteCommand(0x81);
	OLED_WriteCommand(intensity);
}

/**
 * ************************************************************************
 * @brief 全屏内容偏移指定距离
 * 
 * @param[in] shift_num  偏移距离（0~63）
 * 
 * ************************************************************************
 */
void OLED_Shift(uint8_t shift_num)
{
	for(uint8_t i = 0; i < shift_num; i++)
	{
		OLED_WriteCommand(0xd3);//设置显示偏移，垂直向上偏移
		OLED_WriteCommand(i);//偏移量
		HAL_Delay(10);//延时时间
	}
}

/**
 * ************************************************************************
 * @brief 屏幕内容水平方向滚动播放
 * 
 * @param[in] start_page  	开始页数	(0~7)
 * @param[in] end_page  	结束页数	(0~7)
 * @param[in] direction  	滚动方向
 * 								左		LEFT	0x27
 * 								右		RIGHT	0x26
 * @note 在开始页数和结束页数之间的内容才会滚动播放，且写命令顺序不得改变
 * ************************************************************************
 */
void OLED_HorizontalShift(uint8_t start_page,uint8_t end_page,uint8_t direction)
{
	OLED_WriteCommand(0x2e);  //关闭滚动

	OLED_WriteCommand(direction);//设置滚动方向
	OLED_WriteCommand(0x00);//虚拟字节设置，默认为0x00
	OLED_WriteCommand(start_page);//设置开始页地址
	OLED_WriteCommand(0x05);//设置每个滚动步骤之间的时间间隔的帧频
	OLED_WriteCommand(end_page);//设置结束页地址
	OLED_WriteCommand(0x00);//虚拟字节设置，默认为0x00
	OLED_WriteCommand(0xff);//虚拟字节设置，默认为0xff

	OLED_WriteCommand(0x2f);//开启滚动-0x2f，禁用滚动-0x2e，禁用需要重写数据
}

